//
// Created by ly-zhouxyi on 2024/3/5.
//

#include "FFPlayer.h"
#include "AudioDecoder.h"


FFPlayer::FFPlayer(FFmpegCallJava *callJava, PlayStatus *playStatus) : callJava(callJava),
                                                                       playStatus(playStatus) {
    pthread_mutex_init(&seek_mutex, NULL);
    pthread_mutex_init(&init_mutex, NULL);
}

void *decodeFFmpeg(void *data) {
    auto *ffPlayer = (FFPlayer *) data;
    ffPlayer->decodeFFmpegThread();
    pthread_exit(&ffPlayer->decodeThread);
}


void FFPlayer::setDataSource(const char *url) {
    FFPlayer::url = url;
}

void FFPlayer::prepare() {
    pthread_create(&decodeThread, NULL, decodeFFmpeg, this);
}

void FFPlayer::start() {

    if (audioDecoder != nullptr) {
        audioDecoder->play();
    }
    if (videoDecoder != nullptr) {
        videoDecoder->play();
        videoDecoder->audioDecoder = audioDecoder;
    }
    int count = 0;

    while (playStatus != nullptr && !playStatus->exit) {
        // seek  耗时
        if (playStatus->seek) {
            continue;
        }
        // 放入队列 队列太多数据未解码等一等
        if (audioDecoder != nullptr && audioDecoder->audioQueue->getQueueSize() > 50
            || videoDecoder != nullptr && videoDecoder->videoQueue->getQueueSize() > 50) {
            continue;
        }

        AVPacket *avPacket = av_packet_alloc();
        if (av_read_frame(pFormatCtx, avPacket) == 0) {
            if (audioDecoder != nullptr && avPacket->stream_index == audioDecoder->streamIndex) {
                //解码操作
                count++;
                audioDecoder->audioQueue->putAvPacket(avPacket);
            } else if (videoDecoder != nullptr &&
                       avPacket->stream_index == videoDecoder->streamIndex) {
                videoDecoder->videoQueue->putAvPacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
            }
        } else {
            av_packet_free(&avPacket);
            av_free(avPacket);
            while (playStatus != nullptr && !playStatus->exit) {
                // 队列还有数据
                if (audioDecoder != nullptr && audioDecoder->audioQueue->getQueueSize() > 0
                    || videoDecoder != nullptr && videoDecoder->videoQueue->getQueueSize() > 0) {
                    continue;
                } else {
                    playStatus->exit = true;
                    break;
                }
            }
        }
    }
}

void FFPlayer::pause() {
    if (audioDecoder != NULL) {
        audioDecoder->pause();
    }
    if (videoDecoder != NULL) {
        videoDecoder->pause();
    }
}

void FFPlayer::resume() {
    if (audioDecoder != NULL) {
        audioDecoder->resume();
    }
    if (videoDecoder != NULL) {
        videoDecoder->resume();
    }
}

void FFPlayer::stop() {
    playStatus->exit = true;
    int sleepCount = 0;
    pthread_mutex_lock(&init_mutex);
    while (!exit){
        if(sleepCount > 1000){
            exit = true;
        }
        sleepCount++;
        av_usleep(1000 * 10);//暂停10毫秒
    }

    if(audioDecoder != NULL){
        audioDecoder->release();
        delete(audioDecoder);
        audioDecoder = NULL;
    }
    if(videoDecoder != NULL){
        videoDecoder->release();
        delete(videoDecoder);
        videoDecoder = NULL;
    }
    if(pFormatCtx != NULL){
        LOGE("释放 封装格式上下文");
        avformat_close_input(&pFormatCtx);
        avformat_free_context(pFormatCtx);
        pFormatCtx = NULL;
    }
    if(callJava != NULL){
        LOGE("释放 callJava");
        callJava = NULL;
    }
    if(playStatus != NULL){
        LOGE("释放 playStatus");
        playStatus = NULL;
    }
    pthread_mutex_unlock(&init_mutex);
}

void FFPlayer::setVolume(float volume) {
    if (audioDecoder != NULL) {
        audioDecoder->setVolume(volume);
    }
}


void FFPlayer::setMute(int channel) {
    if (audioDecoder != NULL) {
        audioDecoder->setMute(channel);
    }
}

void FFPlayer::setVolumeMute(bool mute) {
    if (audioDecoder != NULL) {
        audioDecoder->setVolumeMute(mute);
    }
}

int FFPlayer::getCurrentVolume() {
    if (audioDecoder != NULL) {
      return  audioDecoder->getCurrentVolume();
    }
    return -10000;
}

void FFPlayer::seek(uint64_t second) {
    if (duration < 0) {
        return;
    }
    if (second >= 0 && second <= duration) {
        playStatus->seek = true;
        // seek 耗时所以需要线程锁
        pthread_mutex_lock(&seek_mutex);
        int64_t rel = second * AV_TIME_BASE;
        LOGE(" time %d", rel);
        avformat_seek_file(pFormatCtx, -1, INT64_MIN, rel, INT64_MAX, 0);
        if (audioDecoder != NULL) {
            audioDecoder->audioQueue->clearAvPacket();
            audioDecoder->clock = 0;
            audioDecoder->last_time = 0;
            avcodec_flush_buffers(audioDecoder->avCodecContext);
        }
        if (videoDecoder != NULL) {
            videoDecoder->videoQueue->clearAvPacket();
            pthread_mutex_lock(&videoDecoder->codecMutex);
            avcodec_flush_buffers(videoDecoder->avCodecContext);
            pthread_mutex_unlock(&videoDecoder->codecMutex);
        }
        pthread_mutex_unlock(&seek_mutex);
        playStatus->seek = false;
    }
}

void FFPlayer::setSpeed(float speed) {
    // 速度调节以 音频为准
    if (audioDecoder != NULL) {
        audioDecoder->setSpeed(speed);
    }
}

void FFPlayer::setPitch(float pitch) {
    if (audioDecoder != NULL) {
        audioDecoder->setPitch(pitch);
    }
}


void FFPlayer::decodeFFmpegThread() {
    // 初始化线程加锁
    pthread_mutex_lock(&init_mutex);
    // 初始网络
    avformat_network_init();
    // 获取上下文
    pFormatCtx = avformat_alloc_context();
    if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {
        LOGE("文件打开失败 url = %s ", url);
        return;
    }
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE("查找文件流失败  url =  %s", url);
        return;
    }
    for (int i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            // 得到音频流
            if (audioDecoder == NULL) {
                duration = pFormatCtx->duration / AV_TIME_BASE;
                audioDecoder = new AudioDecoder(callJava, playStatus,
                                                pFormatCtx->streams[i]->codecpar->sample_rate);
                audioDecoder->streamIndex = i;
                audioDecoder->avCodecParameters = pFormatCtx->streams[i]->codecpar;
                audioDecoder->duration = duration;
                audioDecoder->time_base = pFormatCtx->streams[i]->time_base;
            }
        } else if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            if (videoDecoder == NULL) {
                videoDecoder = new VideoDecoder(callJava, playStatus);
                videoDecoder->streamIndex = i;
                videoDecoder->avCodecParameters = pFormatCtx->streams[i]->codecpar;
                videoDecoder->time_base = pFormatCtx->streams[i]->time_base;

                int num = pFormatCtx->streams[i]->avg_frame_rate.num;
                int den = pFormatCtx->streams[i]->avg_frame_rate.den;
                if (num != 0 && den != 0) {
                    // 采样率  [25,1] [50,2]
                    int fps = num / den;
                    videoDecoder->defaultDelayTime = 1.0 / fps;//秒
                }
            }
        }
    }
    if (audioDecoder != NULL) {
        getCodeContext(&audioDecoder->avCodecContext, audioDecoder->avCodecParameters);
    }
    if (videoDecoder != NULL) {
        getCodeContext(&videoDecoder->avCodecContext, videoDecoder->avCodecParameters);
    }
    callJava->onPrepare();
    pthread_mutex_unlock(&init_mutex);
}


int
FFPlayer::getCodeContext(AVCodecContext **pAvCodecContext, AVCodecParameters *avCodecParameters) {
    // 获取解码器
    AVCodec *avCodec = avcodec_find_decoder(avCodecParameters->codec_id);
    if (!avCodec) {
        LOGE("获取解码器失败  avCodec ")
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    // 获取解码器上下文
    *pAvCodecContext = avcodec_alloc_context3(avCodec);
    if (!audioDecoder->avCodecContext) {
        LOGE("获取解码器上下文失败  pAvCodecContext ")
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    if (avcodec_parameters_to_context(*pAvCodecContext, avCodecParameters) < 0) {
        LOGE("初始化失败 avcodec_parameters_to_context")
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    if (avcodec_open2(*pAvCodecContext, avCodec, NULL) != 0) {
        LOGE("初始化失败  avcodec_open2")
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    LOGE("初始化成功  getCodeContext")
    return 0;
}


FFPlayer::~FFPlayer() {
    pthread_mutex_destroy(&seek_mutex);
    pthread_mutex_destroy(&init_mutex);
}



